﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a seller.
    /// </summary>
    public class CreateSeller : CreateAccountProfile<SellerEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .Rule((ctx, uow) => ctx.Account(uow).EntityID == AccountID);
            }
        }

        /// <summary>
        /// Gets or sets the display name.
        /// </summary>
        public string DisplayName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public SellerFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, SellerEntity entity)
        {
            base.Construct(uow, entity);

            entity.DisplayName = StringHelper.EnsureNotNull(DisplayName).TrimToLen(100);
            entity.Flags = Flags;
        }

        protected override void Work(IUnitOfWork uow, out SellerEntity result)
        {
            base.Work(uow, out result);
            try
            {
                uow.Execute(new EnqueueSellerNotification
                {
                    SellerID = result.EntityID,
                    RecipientID = result.EntityID
                });
            }
            catch(Exception ex)
            {
                uow.Execute(new CreateLogRecordFromException
                {
                    Ex = ex
                });
            }
        }

        #endregion
    }

    /// <summary>
    /// Updates the seller.
    /// </summary>
    public class UpdateSeller : UpdateAccountProfile<SellerEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .Rule((ctx, uow) =>
                    {
                        var seller = ctx.Seller(uow);
                        return seller != null && seller.EntityID == EntityID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets the display name.
        /// </summary>
        public string DisplayName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public SellerFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, SellerEntity entity)
        {
            base.Revise(uow, entity);

            entity.DisplayName = StringHelper.EnsureNotNull(DisplayName).TrimToLen(100);
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the seller statistics.
    /// </summary>
    public class UpdateSellerStats : OperateEntityByID<SellerEntity>
    {
        protected override void Work(IUnitOfWork uow, out SellerEntity result)
        {
            var seller = uow.Execute(new GetSeller
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result;

            uow.Flush();
            uow.SqlQuery("exec fwk_Sellers_UpdateStats @SellerID=:SellerID, @UpdatedOn=:UpdatedOn")
                .SetInt32("SellerID", seller.EntityID)
                .SetDateTime("UpdatedOn", DateTime.UtcNow)
                .ExecuteUpdate();
            uow.Flush();
            
            result = uow.Execute(new RefreshEntity<SellerEntity>
            {
                Entity = seller
            }).Result;
        }
    }

    /// <summary>
    /// Deletes the seller.
    /// </summary>
    public class DeleteSeller : DeleteAccountProfile<SellerEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, SellerEntity entity)
        {
            foreach(var i in entity.Ratings)
            {
                uow.Execute(new DeleteSellerRating
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Comments)
            {
                uow.Execute(new DeleteSellerComment
                {
                    EntityID = i.EntityID
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the seller by identifier.
    /// </summary>
    public class GetSeller : GetAccountProfile<SellerEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the seller by identifier.
    /// </summary>
    public class GetSellerByAccount : GetAccountProfileByAccount<SellerEntity>,
        IApiOperation
    {
        protected override bool Cacheable
        {
	        get
	        {
	            return true;
	        }
        }
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over seller collection.
    /// </summary>
    public class SearchSellers : SearchOperation<SellerEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchSellers()
        {
            Term = SearchTerm.Empty;
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                SellerSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public SellerFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public SellerSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<SellerEntity> Build(IUnitOfWork uow, IQueryable<SellerEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Fetch(x => x.Account);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x => x.DisplayName.StartsWith(Term.Keyword) ||
                        x.Account.Email.StartsWith(Term.Keyword) ||
                        x.Account.Username.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x => x.DisplayName.EndsWith(Term.Keyword) ||
                        x.Account.Email.EndsWith(Term.Keyword) ||
                        x.Account.Username.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x => x.DisplayName.Contains(Term.Keyword) ||
                        x.Account.Email.Contains(Term.Keyword) ||
                        x.Account.Username.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x => x.DisplayName.Equals(Term.Keyword) ||
                        x.Account.Email.Equals(Term.Keyword) ||
                        x.Account.Username.Equals(Term.Keyword));
                    break;
            }
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case SellerSortBy.ReviewCount:
                            query = query.OrderBy(x => x.RatingCount, SortDir, i != 0);
                            break;
                        case SellerSortBy.RatingAverage:
                            query = query.OrderBy(x => x.RatingAverage, SortDir, i != 0);
                            break;
                        case SellerSortBy.DisplayName:
                            query = query.OrderBy(x => x.DisplayName, SortDir, i != 0);
                            break;
                        case SellerSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the collection of file owners as a dictionary, where 
    /// key - seller identifier and value - account email.
    /// </summary>
    public class GetSellerDictionary : SearchOperation<SellerEntity, Int32, String>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public GetSellerDictionary()
        {
            Term = SearchTerm.Empty;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public SellerFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<SellerEntity> Build(IUnitOfWork uow, IQueryable<SellerEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Fetch(x => x.Account);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x =>
                        x.DisplayName.StartsWith(Term.Keyword) ||
                        x.Account.Email.StartsWith(Term.Keyword) ||
                        x.Account.Username.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x =>
                        x.DisplayName.EndsWith(Term.Keyword) ||
                        x.Account.Email.EndsWith(Term.Keyword) ||
                        x.Account.Username.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x =>
                        x.DisplayName.Contains(Term.Keyword) ||
                        x.Account.Email.Contains(Term.Keyword) ||
                        x.Account.Username.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x =>
                        x.DisplayName.Equals(Term.Keyword) ||
                        x.Account.Email.Equals(Term.Keyword) ||
                        x.Account.Username.Equals(Term.Keyword));
                    break;
            }
            query = query.OrderBy(x => x.Account.Email);
            return query;
        }

        protected override IQueryable<KeyValuePair<Int32, String>> Map(IQueryable<SellerEntity> query)
        {
            return query.Select(x => new KeyValuePair<Int32, String>(x.EntityID, x.Account.Email));
        }

        #endregion
    }

    #endregion

    #region __Notification__

    /// <summary>
    /// Enqueues a seller notification message.
    /// </summary>
    /// <remarks>
    /// Message template code: ISLR
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Seller.Account.EntityID], [T:Seller.Account.Email], [T:Seller.Account.Username], [T:Seller.Account.Guid],
    /// [T:Seller.EntityID], [T:Seller.RatingAverage], [T:Seller.ReviewCount], [T:Seller.DisplayName]
    /// </remarks>
    public class EnqueueSellerNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the seller identifier.
        /// </summary>
        public int SellerID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient account identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var seller = uow.Execute(new GetSeller
            {
                ThrowsException = true,
                EntityID = SellerID
            }).Result;
            var recipient = seller.Account.EntityID == RecipientID ? seller.Account : uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var usrLocale = recipient.LocalizationProfile;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "ISLR",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Seller.Account.EntityID", seller.Account.EntityID.ToString()},
                    {"Seller.Account.Email", seller.Account.Email},
                    {"Seller.Account.Username", seller.Account.Username},
                    {"Seller.Account.Guid", seller.Account.Guid.ToString()},
                    {"Seller.EntityID", seller.EntityID.ToString()},
                    {"Seller.DisplayName", seller.DisplayName},
                    {"Seller.RatingAverage", seller.RatingAverage.ToString()},
                    {"Seller.ReviewCount", seller.RatingCount.ToString()}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    #endregion
}
